package ldh.im.fxbase.richtext;

import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.input.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.HitInfo;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import ldh.im.fxbase.util.ImRichTextUtil;

import java.util.ArrayList;
import java.util.List;

public class ImTextPane extends StackPane {

    private TextFlowExt textFlowExt;
    private Path selectPath = new Path();

    private Rectangle clickPoint = new Rectangle();
    private int start=0, end =0;

    private List<Object> items = new ArrayList();
    private String content;
    private double wrappingWidth = 0d;

    public ImTextPane(TextAlignment textAlignment) {
        this.getStyleClass().add("im-text-pane");
        this.setAlignment(Pos.CENTER_LEFT);
        textFlowExt = new TextFlowExt();
        textFlowExt.setLineSpacing(0);
        if (textAlignment != null) {
            textFlowExt.setTextAlignment(textAlignment);
            if (textAlignment == TextAlignment.RIGHT) {
                this.setAlignment(Pos.CENTER_RIGHT);
            }
        }

        selectPath.setMouseTransparent(true);
        this.setMouseTransparent(false);

        Group group = new Group();
        group.getChildren().addAll(textFlowExt, selectPath);
        this.getChildren().addAll(group);

        AnchorPane.setTopAnchor(group, 2d);
        AnchorPane.setLeftAnchor(group, 2d);
        AnchorPane.setBottomAnchor(group, 2d);
        AnchorPane.setRightAnchor(group, 2d);

        bindEvent();
    }

    public void setImContent(String content) {
        this.content = content;
//        String[] strs = content.split(TextItem.SPLIT_STRING2);
        List<Object> objects = ImRichTextUtil.split(content);
        for(Object obj : objects) {
            if (obj instanceof TextItem) {
                TextItem textItem = (TextItem) obj;
                insertTextItem(textItem);
                items.add(textItem);
            } else {
                String s = obj.toString();
                if (s.equals("")) continue;
                items.add(s);
                Text text = new Text(s);
                text.getStyleClass().add("im-text");
                textFlowExt.getChildren().add(text);
            }
        }
    }

    public void setContent(String content) {
        this.content = content;
        items.add(content);
        Text text = new Text(content);
        text.getStyleClass().add("im-text");
        textFlowExt.getChildren().add(text);
    }

    public void setWrappingWidth(double wrappingWidth) {
        this.wrappingWidth = wrappingWidth;
        if (wrappingWidth > 0) {
            textFlowExt.setMaxWidth(wrappingWidth);
        }
    }

    public void insertTextItem(TextItem textItem) {
        Node node = textItem.createNode();
        if (node instanceof Text && wrappingWidth > 0) {
            ((Text) node).setWrappingWidth(wrappingWidth);
        }
        textFlowExt.getChildren().add(node);
    }

    private void bindEvent() {
        textFlowExt.setOnMousePressed(e->{
            clickPoint.setX(e.getX());
            clickPoint.setY(e.getY());

            ImTextPane.this.requestFocus();
            if (e.isPrimaryButtonDown()) {
                selectPath.getElements().clear();
            }
        });

        textFlowExt.setOnMouseDragged(e->{
            select(e);
        });

        textFlowExt.setOnKeyPressed(e->{
            if(e.isControlDown() && e.getCode() == KeyCode.C) {
                copy();
            }
        });
        this.setOnKeyPressed(e->{
            if(e.isControlDown() && e.getCode() == KeyCode.C) {
                copy();
            }
        });

        this.setOnMouseClicked(e->{
            MouseButton mouseButton = e.getButton();
            if(e.getClickCount() == 2 && mouseButton == MouseButton.PRIMARY) {
                selectAll();
            }
        });

        textFlowExt.focusedProperty().addListener(e->{
            if (!textFlowExt.focusedProperty().get() && !ImTextPane.this.focusedProperty().get()) {
                clearSelect();
            }
        });
    }

    private void selectAll() {
        start = 0;
        end = content.length();
        if (start == end) return;
        selectText(start, end);
    }

    private void select(MouseEvent e) {
        HitInfo characterHit = textFlowExt.hitTest(new Point2D(e.getX(), e.getY()));

        HitInfo startHit = textFlowExt.hitTest(new Point2D(clickPoint.getX(), clickPoint.getY()));
        int start1 = startHit.getCharIndex();

        int end1 = characterHit.getCharIndex();

        if (start1 > end1) {
            int t = end1;
            end1 = start1+1;
            start1 = t;
        } else {
            end1++;
        }
        if (textFlowExt.getLineEndPosition(end1) > textFlowExt.getLineCount()) {
            end1--;
        }
        start = start1;
        end = end1;

        selectText(start, end);
    }

    private void selectText(int start, int end) {
        PathElement[] pes = textFlowExt.getRangeShape(start, end);

        selectPath.setStroke(null);
        selectPath.setFill(Color.BLUEVIOLET);
        selectPath.setOpacity(0.3);
        selectPath.getElements().clear();
        selectPath.getElements().addAll(pes);

        textFlowExt.requestFocus();
    }

    public TextFlowExt getTextFlowExt() {
        return textFlowExt;
    }

    private void copy() {
        StringBuilder sb = new StringBuilder();
        if (!(start >= 0 && end >0)) return;
        int length = 0;
        boolean isStart = false;
        for(Object s : items) {
            if (s instanceof String) {
                String str = (String) s;
                int strLength = str.length();
                if (strLength > start - length && start - length >= 0) {
                    isStart = true;
                    if (strLength >= end - length) {
                        sb.append(str.substring(start-length, end-length));
                        break;
                    } else {
                        sb.append(str.substring(start-length));
                    }
                } else if (strLength >= end-length && isStart) {
                    sb.append(str.substring(0, end-length));
                    break;
                } else if (strLength < end - length && isStart) {
                    sb.append(str);
                }
                length += strLength;
            } else {
                TextItem textItem = (TextItem) s;
                int strLength = 1;
                if (strLength > start - length && start - length >= 0) {
                    isStart = true;
                    if (strLength <= end - length) {
                        sb.append(textItem.getImData());
                    }
                } else if (strLength <= end - length  && isStart) {
                    sb.append(textItem.getImData());
                } else if (isStart)
                    break;
                length += strLength;
            }
        }
        copyData(sb.toString());
    }

    private void clearSelect() {
        selectPath.getElements().clear();
        start = 0;
        end = 0;
    }

    private void copyData(String str) {
        Clipboard clipboard = Clipboard.getSystemClipboard();
        ClipboardContent content = new ClipboardContent();
//        content.putString(COPY_HEADER + str);
        content.putString(str);
        clipboard.setContent(content);
    }

}
